package com.janetfilter.core.attach;

import com.janetfilter.core.utils.DateUtils;
import com.janetfilter.core.utils.ProcessUtils;
import com.janetfilter.core.utils.WhereIsUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 虚拟机选择器类
 * 提供命令行界面，列出系统中运行的Java虚拟机并允许用户选择一个进行附加
 * 使用jps命令获取运行中的Java进程列表
 */
public class VMSelector {
    // 当前JAR文件
    private final File thisJar;
    // 虚拟机描述符列表
    private List<VMDescriptor> descriptors;

    /**
     * 创建虚拟机选择器实例
     * 
     * @param thisJar 当前JAR文件，用于后续附加操作
     */
    public VMSelector(File thisJar) {
        this.thisJar = thisJar;
    }

    /**
     * 获取系统中运行的Java虚拟机列表
     * 使用jps命令执行并解析输出，排除自身进程和jps工具进程
     * 
     * @return 虚拟机描述符列表
     * @throws Exception 如果获取失败
     */
    private List<VMDescriptor> getVMList() throws Exception {
        // 查找jps命令
        File jpsCommand = WhereIsUtils.findJPS();
        if (null == jpsCommand) {
            throw new Exception("jps command not found");
        }

        // 执行jps命令并获取输出
        List<String> list = new ArrayList<>();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ProcessUtils.start(new ProcessBuilder(jpsCommand.getAbsolutePath(), "-lv"), bos);

        // 解析jps命令输出
        String line;
        BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray())));
        while ((line = reader.readLine()) != null) {
            list.add(line);
        }

        // 获取当前进程ID，用于过滤掉自身进程
        String processId = ProcessUtils.currentId();
        // 处理输出，创建VMDescriptor对象并过滤、排序
        return list.stream()
                .map(s -> {
                    String[] section = (s + "   ").split(" ", 3);
                    return new VMDescriptor(section[0].trim(), section[1].trim(), section[2].trim());
                })
                // 过滤掉自身进程和jps工具进程
                .filter(d -> !d.getId().equals(processId) && !"sun.tools.jps.Jps".equals(d.getClassName()) && !"jdk.jcmd/sun.tools.jps.Jps".equals(d.getClassName()))
                // 按进程ID排序
                .sorted(Comparator.comparingInt(d -> Integer.parseInt(d.getId())))
                .collect(Collectors.toList());
    }

    /**
     * 获取用户输入
     * 
     * @return 用户输入的字符串，已去除首尾空格
     * @throws IOException 如果读取输入失败
     */
    private String getInput() throws IOException {
        return new BufferedReader(new InputStreamReader(System.in)).readLine().trim();
    }

    /**
     * 处理用户选择
     * 支持选择进程ID、刷新列表(R/r)和退出(Q/q)
     * 
     * @throws Exception 如果处理过程中发生错误
     */
    private void processSelect() throws Exception {
        System.out.print("  Select: ");
        String input = getInput();

        switch (input) {
            case "Q":
            case "q":
                // 退出程序
                System.exit(0);
            case "R":
            case "r":
                // 刷新列表
                System.out.println("  =========================== " + DateUtils.formatDateTime() + " ============================");
                select();
                return;
            case "":
                // 空输入，重新提示选择
                processSelect();
                return;
            default:
                // 尝试解析为数字
                int index;
                try {
                    index = Integer.parseInt(input);
                } catch (NumberFormatException e) {
                    invalidInput(input);
                    return;
                }

                // 检查数字是否有效
                if (index < 1) {
                    invalidInput(input);
                    return;
                }

                if (index > descriptors.size()) {
                    invalidInput(input);
                    return;
                }

                // 获取附加参数
                System.out.print("  Agent args: ");
                input = getInput();
                try {
                    // 启动附加操作
                    VMLauncher.launch(thisJar, descriptors.get(index - 1), input);
                } catch (Exception e) {
                    System.err.println("> Attach to: " + index + " failed.");
                    e.printStackTrace(System.err);
                    return;
                }
                break;
        }
    }

    /**
     * 处理无效输入
     * 
     * @param input 用户输入的无效字符串
     * @throws Exception 如果处理过程中发生错误
     */
    private void invalidInput(String input) throws Exception {
        System.err.println("> Invalid input: " + input);
        processSelect();
    }

    /**
     * 显示虚拟机列表并处理用户选择
     * 
     * @throws Exception 如果处理过程中发生错误
     */
    public void select() throws Exception {
        // 判断是否是首次获取列表
        boolean first = null == descriptors;
        // 获取最新的虚拟机列表
        List<VMDescriptor> temp = getVMList();
        // 标记新旧进程（用于显示）
        if (null != descriptors && !descriptors.isEmpty()) {
            temp.forEach(d -> d.setOld(descriptors.stream().anyMatch(d1 -> d.getId().equals(d1.getId()))));
        }

        descriptors = temp;
        System.out.println("  Java Virtual Machine List: (Select and attach" + (first ? "" : ", + means the new one") + ")");

        // 显示虚拟机列表
        int index = 1;
        for (VMDescriptor d : descriptors) {
            System.out.printf("  %3d]:%s%s %s%n", index++, d.getOld() ? " " : "+", d.getId(), d.getClassName());
        }
        System.out.println("    r]: <Refresh virtual machine list>");
        System.out.println("    q]: <Quit the ja-netfilter>");

        // 处理用户选择
        processSelect();
    }
}
